﻿using Aliyun.Acs.Core;
using Aliyun.Acs.Core.Profile;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Linq;
using NLS.API.Model;

namespace NLS.API
{
    /// <summary>
    ///     阿里云智能语音交互 - 语音合成
    /// </summary>
    public class NlsTTSClient
    {
        public static IMemoryCache mem = new MemoryCache(new MemoryCacheOptions());
        /// <summary>
        ///     允许的格式
        /// </summary>
        readonly string[] allowFormat = new string[] { "pcm", "wav", "mp3" };
        /// <summary>
        ///     允许的采样率
        /// </summary>
        readonly int[] allowSampleRate = new int[] { 8000, 16000, 24000, 32000 };
        /// <summary>
        ///     允许的发音人
        /// </summary>
        readonly string[] allowVoice = new string[] { "zhimi_emo", "zhiyan_emo", "zhibei_emo", "zhitian_emo", "xiaoyun", "xiaogang", "ruoxi", "siqi", "sijia", "sicheng", "aiqi", "aijia", "aicheng", "aida", "ninger", "ruilin", "siyue", "aiya", "aixia", "aimei", "aiyu", "aiyue", "aijing", "xiaomei", "aina", "yina", "sijing", "sitong", "xiaobei", "aitong", "aiwei", "aibao", "harry", "abby", "andy", "eric", "emily", "luna", "luca", "wendy", "william", "olivia", "shanshan", "chuangirl", "lydia", "aishuo", "qingqing", "cuijie", "xiaoze", "tomoka", "tomoya", "annie", "jiajia", "indah", "taozi", "guijie", "stella", "stanley", "kenny", "rosa", "farah", "mashu", "xiaoxian", "yuer", "maoxiaomei", "zhifei", "zhilun", "aifei", "yaqun", "qiaowei", "dahu", "ava", "ailun", "jielidou", "laotie", "laomei", "aikan", "tala", "tien", "becca" };

        HttpClient _httpClient;
        NlsConfig _nlsConfig;
        IAcsClient _acsClient;

        public NlsTTSClient(IHttpClientFactory httpClientFactory, IOptions<NlsConfig> options)
        {
            _httpClient = httpClientFactory.CreateClient("NLS");
            _nlsConfig = options.Value;
            if (_nlsConfig == null)
            {
                throw new Exception("没有找到NLSConfig配置项");
            }
            if (string.IsNullOrWhiteSpace(_nlsConfig.AppKey))
            {
                throw new Exception("缺少智能语音交互项目Appkey");
            }
            if (string.IsNullOrWhiteSpace(_nlsConfig.AccessKeyID))
            {
                throw new Exception("缺少AccessKeyID");
            }
            if (string.IsNullOrWhiteSpace(_nlsConfig.AccessKeySecret))
            {
                throw new Exception("缺少AccessKeySecret");
            }
            var defaultProfile = DefaultProfile.GetProfile("cn-shanghai", _nlsConfig.AccessKeyID, _nlsConfig.AccessKeySecret);
            _acsClient = new DefaultAcsClient(defaultProfile);
        }


        /// <summary>
        ///     获取音频文件
        /// </summary>
        /// <param name="text">待合成的文本，需要为UTF-8编码。使用GET方法，需要再采用RFC 3986规范进行urlencode编码；使用POST方法不需要urlencode编码。</param>
        /// <param name="format">音频编码格式，支持pcm/wav/mp3格式。默认值：pcm。</param>
        /// <param name="sample_rate">音频采样率，支持16000 Hz和8000 Hz，默认值：16000 Hz。</param>
        /// <param name="voice">发音人,更多发音人请参见接口说明。</param>
        /// <param name="volume">音量，取值范围：0~100</param>
        /// <param name="speech_rate">语速，取值范围：-500~500</param>
        /// <param name="pitch_rate">语调，取值范围：-500~500</param>
        /// <returns></returns>
        public async Task<(byte[], string)> Post(string text, string? format = null, int? sample_rate = null, string? voice = null, int? volume = null, int? speech_rate = null, int? pitch_rate = null)
        {
            // 就这三是必填参数，下面的If判断中的参数都是可选参数
            JObject param = new()
            {
                { "appkey", _nlsConfig.AppKey },
                { "text", text },
                { "token", GetNLSToken() }
            };

            // 如果有格式
            if (string.IsNullOrWhiteSpace(format) == false && allowFormat.Contains(format))
            {
                param.Add("format", format);
            }

            // 如果有采样率
            if (sample_rate != null && allowSampleRate.Contains(sample_rate.Value))
            {
                param.Add("sample_rate", sample_rate);
            }

            // 如果有发音人，具体有哪些：https://help.aliyun.com/document_detail/84435.htm?spm=a2c4g.11186623.0.0.2d8a18194f7sdi#topic-2572243
            if (voice != null && allowVoice.Contains(voice))
            {
                param.Add("voice", voice);
            }

            // 如果有音量，取值范围：0~100
            if (volume != null)
            {
                volume = Math.Max(volume.Value, 0);
                volume = Math.Min(volume.Value, 100);
                param.Add("volume", volume);
            }

            // 如果有语速，取值范围：-500~500
            if (speech_rate != null)
            {
                speech_rate = Math.Max(speech_rate.Value, -500);
                speech_rate = Math.Min(speech_rate.Value, 500);
                param.Add("speech_rate", speech_rate);
            }

            // 如果有语调，取值范围：-500~500
            if (pitch_rate != null)
            {
                pitch_rate = Math.Max(pitch_rate.Value, -500);
                pitch_rate = Math.Min(pitch_rate.Value, 500);
                param.Add("pitch_rate", pitch_rate);
            }

            var paramContent = param.ToJson() ?? "";

            HttpContent content = new StringContent(paramContent, System.Text.Encoding.UTF8, "application/json");
            var requestMessage = new HttpRequestMessage
            {
                RequestUri = new Uri(_nlsConfig.Url),
                Method = HttpMethod.Post,
                Content = content
            };

            var httpResponseMessage = await _httpClient.SendAsync(requestMessage);

            if (httpResponseMessage.IsSuccessStatusCode == false)
            {
                throw new Exception(await httpResponseMessage.Content.ReadAsStringAsync());
            }

            var bytes = await httpResponseMessage.Content.ReadAsByteArrayAsync();
            var mediaType = httpResponseMessage.Content.Headers.ContentType?.MediaType ?? "";

            return (bytes, mediaType);
        }

        /// <summary>
        ///     获取Token
        /// </summary>
        /// <returns></returns>
        public string GetNLSToken()
        {
            mem.TryGetValue("NLSToken", out string token);
            if (string.IsNullOrWhiteSpace(token) == false)
            {
                return token;
            }
            CommonRequest request = new()
            {
                Method = Aliyun.Acs.Core.Http.MethodType.POST,
                Domain = "nls-meta.cn-shanghai.aliyuncs.com",
                Version = "2019-02-28",
                Action = "CreateToken"
            };
            var response = _acsClient.GetCommonResponse(request);
            if (response.HttpStatus != 200)
            {
                return string.Empty;
            }
            var tokenResponse = response.Data.ToObject<TokenResponseData>();
            var tokenModel = tokenResponse?.Token;

            if (tokenResponse == null || tokenModel == null)
            {
                return string.Empty;
            }

            var expireDateTime = tokenModel.ExpireTime.GetDateTime();

            // 缓存一下Token
            mem.Set("NLSToken", tokenModel.Id, expireDateTime.AddSeconds(-5));
            return tokenModel.Id;
        }
    }
}
